package com.qf.service.impl.blog;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qf.api.blog.dto.ArticleDTO;
import com.qf.api.blog.entity.*;
import com.qf.api.blog.service.ArticleService;
import com.qf.api.blog.vo.ArticlePageVO;
import com.qf.api.front.dto.ArticleContent;
import com.qf.api.front.vo.ArticleVO;
import com.qf.core.constant.RedisConstant;
import com.qf.core.exception.BaseException;
import com.qf.core.response.ResponseCode;
import com.qf.core.utils.ToolUtils;
import com.qf.dao.blog.mapper.*;
import com.qf.redis.utils.RedisUtils;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author sin
 * @description 针对表【t_article(文章表)】的数据库操作Service实现
 * @createDate 2025-05-23 14:19:29
 */
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, ArticleEntity>
        implements ArticleService {
    @Resource
    private ArticleMapper articleMapper;

    @Resource
    private ArticleContentMapper articleContentMapper;

    @Resource
    private CategoryServiceImpl categoryService;

    @Resource
    private ArticleCategoryMapper articleCategoryMapper;

    @Resource
    private TagMapper tagMapper;

    @Resource
    private ArticleTagMapper articleTagMapper;

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public List<ArticlePageVO> getArticlePage(Integer tagId, Integer categoryId, String articleTitle, Integer isTop, Integer status) {
        // 获取文章的
        // 五张表  构建数据
        //  文章表  文章标签关联表（文章id，标签id）  标签表（标签id，标签名）  文章分类关联表（文章id，分类id）  分类表（分类id，分类名）
        return articleMapper.getArticlePage(tagId, categoryId, articleTitle, isTop, status);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean publishArticle(ArticleDTO articleDTO) {
        try {
            Long categoryId = articleDTO.getCategoryId();
            ArticleEntity articleDO = checkCategory(articleDTO, categoryId);
            articleMapper.insert(articleDO);
            // 3. 拿到插入记录的主键 ID
            Long articleId = articleDO.getId();
            // 4. 插入文章内容
            articleContentMapper.insert(ArticleContentEntity.builder().articleId(articleId).content(articleDTO.getContent()).build());
            // 5. 插入文章关联的分类
            articleCategoryMapper.insert(ArticleCategoryEntity.builder().articleId(articleId).categoryId(categoryId).build());
            // 6. 保存文章关联的标签集合
            insertTags(articleId, articleDTO.getTags());
            return true;
        } catch (Exception e) {
            log.error("发布文章失败，原因：数据库异常");
            throw new BaseException(ResponseCode.ARTICLE_PUBLISH_FAILED);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateArticle(Long articleId, ArticleDTO articleDTO) {
        // 1. 判断文章是否存在
        checkArticleExist(articleId);
        // 2. 判断分类名称是否存在
        Long categoryId = articleDTO.getCategoryId();
        // 3. 构建修改文章信息
        ArticleEntity newArticleDO = checkCategory(articleDTO, categoryId);
        newArticleDO.setId(articleId);
        try {
            // 4. 修改文章信息
            articleMapper.updateById(newArticleDO);
            // 5. 修改文章内容
            articleContentMapper.updateByArticleId(articleId, articleDTO.getContent());
            // 6. 修改文章关联的分类
            articleCategoryMapper.updateByArticleId(articleId, categoryId);
            // 8. 删除文章关联的标签
            articleTagMapper.deleteByArticleId(articleId);
            // 9. 保存文章关联的标签集合
            insertTags(articleId, articleDTO.getTags());
            return true;
        } catch (Exception e) {
            log.error("修改文章失败，原因：数据库异常");
            throw new BaseException(ResponseCode.ARTICLE_PUBLISH_FAILED);
        }
    }

    @Override
    public Boolean deleteArticleById(Long id) {
        checkArticleExist(id);
        return articleMapper.deleteById(id) > 0;
    }

    @Override
    public Boolean updateArticleStatus(Long articleId, Integer type, Integer value) {
        if (type == 0) {
            articleMapper.updateArticleTop(articleId, value);
        } else {
            articleMapper.updateArticleStatus(articleId, value);
        }
        return true;
    }

    @Override
    public ArticleDTO getArticleById(Long id) {
        ArticleEntity articleDO = checkArticleExist(id);
        Long contentId = articleContentMapper.selectContentIdByArticleId(id);
        ArticleContentEntity articleContentDO = articleContentMapper.selectById(contentId);
        Long categoryId = articleCategoryMapper.selectByArticleId(id);
        List<String> tags = tagMapper.selectTagByArticleId(id);
        ArticleDTO articleDTO = BeanUtil.copyProperties(articleDO, ArticleDTO.class);
        articleDTO.setContent(articleContentDO.getContent());
        articleDTO.setCategoryId(categoryId);
        articleDTO.setTags(tags);
        return articleDTO;
    }

    @Override
    public Boolean checkArticleTitle(String title) {
        ArticleEntity articleDO = articleMapper.selectByTitle(title.trim());
        return Objects.nonNull(articleDO);
    }

    @Override
    public long getWordCount() {
        return articleMapper.getWordCount();
    }

    /**
     * 博客前台分页
     **/
    @Override
    public List<ArticleVO> getPageArticle(String title, Long tagId, Long categoryId) {
        return articleMapper.getPageArticle(title, tagId, categoryId);
    }

    @Override
    public ArticleContent getArticleContent(Long articleId) {
        return articleMapper.getArticleContent(articleId);
    }

    /**
     * 记录文章访问
     **/
    @Override
    public void addArticleVisitTime(String ip, Long articleId) {
        boolean res = redisUtils.setIfAbsent(RedisConstant.VISITOR_ARTICLE_IP + articleId + ":" + ip,"1",24, TimeUnit.HOURS);
        if(res) {
            // MySQL 同步
            System.out.println(articleId + ": +1");
            UpdateWrapper<ArticleEntity> wrapper = new UpdateWrapper<>();
            wrapper.setSql("visit_count = visit_count + 1");
            wrapper.eq("id", articleId);
            this.update(wrapper);
        }
    }

    /**
     * 判断文章是否存在
     **/
    private ArticleEntity checkArticleExist(Long id) {
        ArticleEntity articleDO = articleMapper.selectById(id);
        // "!null->true;null->false"
        if (!Objects.nonNull(articleDO)) {
            throw new BaseException(ResponseCode.ARTICLE_NOT_FOUND);
        }
        return articleDO;
    }

    /**
     * 判断分类是否存在
     **/
    private ArticleEntity checkCategory(ArticleDTO articleDTO, Long categoryId) {
        // 1. 处理文章关联的分类
        CategoryEntity categoryDO = categoryService.getById(categoryId);
        // 2. 判断分类名称是否存在
        if (Objects.isNull(categoryDO)) {
            throw new BaseException(ResponseCode.CATEGORY_NOT_EXISTED);
        }
        return ArticleEntity.builder().
                title(articleDTO.getTitle()).
                author(articleDTO.getAuthor()).
                cover(articleDTO.getCover()).
                summary(articleDTO.getSummary()).
                wordCount(ToolUtils.getWords(articleDTO.getContent())).
                isTop(articleDTO.getIsTop()).
                status(articleDTO.getStatus()).
                build();
    }

    /**
     * 保存标签
     *
     * @param articleId 文章Id tagList 标签集合
     */
    private void insertTags(Long articleId, List<String> tagList) {
        // 1. 创建两个集合来存放已存在的标签和新增的标签
        Set<String> existTags = new HashSet<>();
        List<String> notExistTag;
        // 2. 查询出所有标签
        List<TagEntity> tagDOS = tagMapper.selectList(null);
        // 3. 如果表中还没有添加任何标签
        if (CollectionUtils.isEmpty(tagDOS)) {
            notExistTag = tagList;
        } else {
            // 表中已添加相关标签，则需要筛选
            List<String> tagNames = tagDOS.stream().map(tagDO -> String.valueOf(tagDO.getName())).toList();
            // 不存在的
            notExistTag = tagList.stream().filter(tagName -> !tagNames.contains(tagName)).collect(Collectors.toList());
            // 按字符串名称提交上来的标签，也有可能是表中已存在的，比如表中已经有了 Java 标签，用户提交了个 java 小写的标签，需要内部装换为 Java 标签
            Map<String, Long> tagNameIdMap = tagDOS.stream().collect(Collectors.toMap(tagDO -> tagDO.getName().toLowerCase(), TagEntity::getId));
            // 已存在的标签存入HashSet
            tagList.forEach(tagName -> {
                if (tagNames.contains(tagName)) {
                    // 若提交的标签与表中的标签 ID 相同，则表示该标签是已存在的
                    existTags.add(String.valueOf(tagNameIdMap.get(tagName.toLowerCase())));
                }
            });
            // 使用迭代器进行安全的删除操作
            Iterator<String> iterator = notExistTag.iterator();
            while (iterator.hasNext()) {
                String tagName = iterator.next();
                // 转小写, 若 Map 中相同的 key，则表示该新标签是重复标签
                if (tagNameIdMap.containsKey(tagName.toLowerCase())) {
                    // 从不存在的标签集合中清除
                    iterator.remove();
                    // 并将对应的 ID 添加到已存在的标签集合
                    existTags.add(String.valueOf(tagNameIdMap.get(tagName.toLowerCase())));
                }
            }
        }
        // 4. 批量插入标签到文章标签关联表中
        if (!CollectionUtils.isEmpty(existTags)) {
            List<ArticleTagEntity> ArticleTagEntityS = existTags.stream().map(tagName -> ArticleTagEntity.builder().articleId(articleId).tagId(Long.valueOf(tagName)).build()).collect(Collectors.toList());
            articleTagMapper.insertBatchSomeColumn(ArticleTagEntityS);
        }
        // 5. 将不存在的标签插入标签库 并获取到对应的标签Id
        if (!CollectionUtils.isEmpty(notExistTag)) {
            List<ArticleTagEntity> articleTagIds = new ArrayList<>();
            notExistTag.forEach(tag -> {
                TagEntity tagDO = TagEntity.builder().name(tag).build();
                tagMapper.insert(tagDO);
                ArticleTagEntity articleTagEntity = ArticleTagEntity.builder().articleId(articleId).tagId(tagDO.getId()).build();
                articleTagIds.add(articleTagEntity);
            });
            // 6. 批量插入新入库的标签到文章标签关联表中
            articleTagMapper.insertBatchSomeColumn(articleTagIds);
        }
    }
}




